
(function ($) {

  Drupal.nodejschat = Drupal.nodejschat || {'initialised' : false, chats: {}, callbacks: {}};

  Drupal.nodejschat.initialiseChat = function(chat) {
    if (chat.latestMsgId > 0) {
      var targetOffset = $('#nodejschat-board-' + chat.cid + ' div.new-message:last').offset().top;
      var boardOffset = $('#nodejschat-board-' + chat.cid).offset().top;
      var scrollAmount = targetOffset - boardOffset;
      $('#nodejschat-board-' + chat.cid).animate({scrollTop: '+='+ scrollAmount +'px'}, 250);
      $('#nodejschat-board-' + chat.cid + '.new-message').removeClass('new-message');
    }

    $('#edit-nodejschat-entry-message-' + chat.cid).keyup(function(e) {
      var chat = Drupal.settings.nodejschat.chats[this.id.replace(/^edit-nodejschat-entry-message-/, '')];
      var messageText = $('#edit-nodejschat-entry-message-' + chat.cid).val().replace(/^\s+|\s+$/g, '');
      var anonNameText = '';
      if ($('#edit-nodejschat-entry-anon-name-' + chat.cid)) {
        anonNameText = $('#edit-nodejschat-entry-anon-name-' + chat.cid).val();
      }
      if (messageText && e.keyCode == 13 && !e.shiftKey && !e.ctrlKey) {
        Drupal.nodejschat.postMessage(messageText, anonNameText, chat);
        $('#edit-nodejschat-entry-message-' + chat.cid).val('').focus();
      }
      else {
        return true;
      }
    });

    $('#edit-nodejschat-entry-submit-' + chat.cid).click(function (e) {
      var chat = Drupal.settings.nodejschat.chats[this.id.replace(/^edit-nodejschat-entry-submit-/, '')];
      e.preventDefault();
      e.stopPropagation();

      var messageText = $('#edit-nodejschat-entry-message-' + chat.cid).val().replace(/^\s+|\s+$/g, '');
      var anonNameText = '';
      if ($('#edit-nodejschat-entry-anon-name-' + chat.cid)) {
        anonNameText = $('#edit-nodejschat-entry-anon-name-' + chat.cid).val();
      }
      if (messageText) {
        Drupal.nodejschat.postMessage(messageText, anonNameText, chat);
        $('#edit-nodejschat-entry-message-' + chat.cid).val('').focus();
      }
    });

    $('#nodejschat-board-' + chat.cid).scroll(function() {
      var chat = Drupal.settings.nodejschat.chats[this.id.replace(/^nodejschat-board-/, '')];
      var yPos = $('#nodejschat-board-' + chat.cid).scrollTop();
      if (yPos === 0) {
        Drupal.nodejschat.getPreviousMessages(chat.cid, chat.prevMsgId, chat.limit);
      }
    });
  }

  /**
   * Provide js API functions for modules that want to create nodejschats
   * from js events.
   */
  Drupal.nodejschat.createChat = function(options, callback) {
    if (typeof options.uid === 'undefined') {
      options.uid = Drupal.settings.nodejschat.uid;
    }
    if (typeof options.public === 'undefined') {
      options.public = 0;
    }
    $.ajax({
      type: 'POST',
      url: Drupal.settings.nodejschat.createChatPath,
      dataType: 'json',
      success: function (data, textStatus, XHR) {
        callback(data.data);
      },
      data: {
        nodejschat: options,
        formToken: Drupal.settings.nodejschat.createChatToken,
        formId: Drupal.settings.nodejschat.createChatFormId
      }
    });
  }

  /**
   * We depend on the Nodejs module successfully create a socket for us.
    */
  Drupal.Nodejs.connectionSetupHandlers.nodejschat = {
    connect: function () {
      for (var cid in Drupal.settings.nodejschat.chats) {
        Drupal.nodejschat.initialiseChat(Drupal.settings.nodejschat.chats[cid]);
      }
      Drupal.nodejschat.initialised = true;
    }
  };

  Drupal.nodejschat.postMessage = function(message, anonName, chat) {
    var formId = 'nodejschat_form_token_' + chat.cid;
    if (typeof chat.formId !== 'undefined') {
      formId = chat.formId;
    }
    var formTokenDomId = '#edit-nodejschat-entry-form-token-' + chat.cid;
    if (typeof chat.formTokenDomId !== 'undefined') {
      formTokenDomId = chat.formTokenDomId;
    }

    var data = {
      message: message,
      anonName: anonName,
      formToken: $(formTokenDomId).val(),
      formId: formId
    };

    Drupal.nodejschat.runCallbacks(chat, data);

    $.ajax({
      type: 'POST',
      url: Drupal.settings.nodejschat.postMessagePath + '/' + chat.cid,
      dataType: 'json',
      success: function () {},
      data: data
    });
  }

  Drupal.nodejschat.getPreviousMessages = function(cid, cmid, limit) {
    if (limit === undefined) limit = 20;

    $.ajax({
      type: 'GET',
      url: Drupal.settings.nodejschat.nodejschatPath + '/' + cid + '/messages/previous/' + cmid + '/' + limit,
      success: function(data) {
        /**
         * Shamelessly stolen and adapted from here:
         *
         * http://stackoverflow.com/questions/5688362/how-to-prevent-scrolling-on-prepend
         *
         * Better implementation ideas welcome.
         */
        var currentTop = $('#nodejschat-board-' + cid).children().first();
        for (var message in data) {
          Drupal.nodejschat.addPreviousMessageToBoard(data[message]);
        }
        var previousHeight = 0;
        currentTop.prevAll().each(function() {
          previousHeight += $(this).outerHeight();
        });
        $('#nodejschat-board-' + cid).animate({scrollTop: '+='+ previousHeight +'px'}, 10);
      }
    });
  }

  Drupal.nodejschat.updateUserList = function(message) {
    if ($('#nodejschat-user-' + message.cid + '-' + message.uid).length == 0) {
      var userHtml = '<li style="display: none;" id="nodejschat-user-'
                     + message.cid + '-' + message.uid + '"><a href="/user/'
                     + message.uid + '">' + message.name + '</a></li>';
      $(userHtml).hide().appendTo($('#nodejschat-chat-user-list-' + message.cid)).show('normal');
    }
  }

  Drupal.nodejschat.addMessageToBoard = function(message) {
    $('#nodejschat-board-' + message.cid).append(message.msg);
    Drupal.nodejschat.scrollToLatestMessage(message.cid);
  }

  Drupal.nodejschat.addPreviousMessageToBoard = function(message) {
    var chat = Drupal.settings.nodejschat.chats[message.cid];
    chat.prevMsgId = message.cmid;
    Drupal.settings.nodejschat.chats[message.cid] = chat;
    $('#nodejschat-board-' + message.cid).prepend(message.msg);
  }

  Drupal.nodejschat.scrollToLatestMessage = function(cid) {
    var boardOffset = $('#nodejschat-board-' + cid).offset().top;
    var targetOffset = $('#nodejschat-board-' + cid + ' div.new-message:last').offset().top;
    var scrollAmount = targetOffset - boardOffset;
    $('#nodejschat-board-' + cid).animate({scrollTop: '+='+ scrollAmount +'px'}, 250);
    $('#nodejschat-board-' + cid + ' .new-message').removeClass('new-message');
  }

  Drupal.Nodejs.callbacks.nodejschatMessageHandler = {
    callback: function (message) {
      Drupal.nodejschat.addMessageToBoard(message.data);
    }
  };

  Drupal.Nodejs.callbacks.nodejschatUserOnlineHandler = {
    callback: function (message) {
      Drupal.nodejschat.updateUserList(message.data);
    }
  };

  Drupal.Nodejs.contentChannelNotificationCallbacks.nodejschat = {
    callback: function (message) {
      if (message.data.type == 'disconnect') {
        var cid = message.channel.replace(/^nodejschat_/, '');
        if ($('#nodejschat-user-' + cid + '-' + message.data.uid).length == 1) {
          $('#nodejschat-user-' + cid + '-' + message.data.uid).hide('normal', function () {
            $('#nodejschat-user-' + cid + '-' + message.data.uid).remove();
          });
        }
      }
    }
  };
  Drupal.nodejschat.runCallbacks = function (nodejschat, message) {
    if (nodejschat.callback) {
      if (typeof nodejschat.callback == 'string') {
        nodejschat.callback = [nodejschat.callback];
      }
      $.each(nodejschat.callback, function () {
        var callback = this;
        if (Drupal.nodejschat.callbacks[callback] && $.isFunction(Drupal.nodejschat.callbacks[callback].callback)) {
          try {
            Drupal.nodejschat.callbacks[callback].callback(nodejschat, message);
          }
          catch (exception) {}
        }
      });
    }
    else {
      $.each(Drupal.nodejschat.callbacks, function () {
        if ($.isFunction(this.callback)) {
          try {
            this.callback(nodejschat, message);
          }
          catch (exception) {}
        }
      });
    }
  };
})(jQuery);